import { TAllTypes, IFunctionCalculate, ISpecial, IValueNumber, IAllPosition, IOperate, IFunction, IExcelValue, IGetRangeValues } from './types';
import { parse } from './parse';
import { rebuildCaculate } from './rebuild-calculate';
import { FormulaError } from './formula-error';
import { safeWhile } from './utils';
import { calculate } from './calculate';
import { Range } from './range';
import { ITableDatasPureData } from '../../cors/table-datas.interface';
import { FunctionManange } from './functions';
import {  } from './functions/';
export abstract class AFormula implements IGetRangeValues {
  public abstract getRangeValues(range: TRange): (null | string | number | boolean)[][];
  protected abstract tableDatas: ITableDatasPureData;
  protected abstract factory: (args: any) => any;
  protected abstract getPositionValue(val: IAllPosition): (TAllTypes)[];
  protected calcArr: TAllTypes[] = [];
  protected result: TAllTypes;
  protected functionManage: FunctionManange;
  protected abstract funcCalculate: IFunctionCalculate;
  constructor() {
    
  }
  public init() {
    this.functionManage = this.factory(FunctionManange);
  }
  public updateFormulaStr(str: string, parseCallback: (val: TAllTypes[]) => boolean = () => true) {
    // console.log(parse(str))
    let parseRes = parse(str);

    let _temp = rebuildCaculate(parseRes);
    this.calcArr = this.preHandleRange(_temp);
    let canContinu = parseCallback(this.calcArr);
    if(!calculate) {
      return new FormulaError('#VALUE!', {errorId: '374c6d44aef0'});
    }
    this.valueChange();
    return this.result;
  };
  protected preHandleRange(val: TAllTypes[]) {
    safeWhile(() => {
      for(let i = 0; i < val.length; i++) {
        if(val[i].type === 'special' && (val[i] as ISpecial).value === ':' ) {
          let range = this.rangeFactory(val[i - 2] as IAllPosition, val[i - 1] as IAllPosition);
          val.splice(i - 2, 3, range);
          return false;
        }
      }
      return true;
    })
    return val;
  }
  public valueOf() {
    if (this.result.type === 'value') {
      return this.result.value;
    }
  }
  public toString() {
    if (!this.result && this.result.type === 'value') {
      return this.result.value;
    }
    return '';
  }
  public valueChange() {
    let copyCalcArr = [...this.calcArr];
    try {
      safeWhile(() => {
        if (copyCalcArr.length < 2) {
          return true;
        }
        for (let i = 0; i < copyCalcArr.length; i++) {
          if (copyCalcArr[i].type !== 'value') {
            if (copyCalcArr[i].type === 'special' && (copyCalcArr[i] as ISpecial).value === ':') {
              if ((copyCalcArr[i] as ISpecial).value === ':') {
                let result = this.rangeFactory(copyCalcArr[i - 2] as IAllPosition, copyCalcArr[i - 1] as IAllPosition);
                // @ts-ignore
                copyCalcArr.splice(0, 3, result);
                return false;
              }
              if ((copyCalcArr[i] as ISpecial).value === ',') {
                return false;
              }
            }
            if (copyCalcArr[i].type === 'link') {
              throw new Error('link not implement')
            }
            if (copyCalcArr[i].type === 'operate') {
              let val1 = this.getValue(copyCalcArr[i - 2]) as IValueNumber[];
              let val2 = this.getValue(copyCalcArr[i - 1]) as IValueNumber[];
              if (val1.length !== 1 || val2.length !== 1) {
                throw new FormulaError('#REF!', { info: [copyCalcArr[i - 2], copyCalcArr[i - 1]], errorId: 'e4836f48c363' });
              }
              let _result = calculate((copyCalcArr[i] as IOperate).value, [val1[0], val2[0]]);
              copyCalcArr.splice(0, 3, _result);
              return false;
            }
            if (copyCalcArr[i].type === 'function') {
              let vals = copyCalcArr.slice(0, i);
              let funcName = (copyCalcArr[i] as IFunction).value;
              let result = this.matchFuncCalculate(funcName, vals);
              copyCalcArr.splice(0, i + 1, ...result);
              return false;
            }
            // throw new FormulaError('#UNKOWN!', { info: { msg: '未匹配任何操作', value: copyCalcArr[i] } });
          }
        }
      }, 10000)
    } catch (error) {
      console.log(copyCalcArr)
      console.log(error)
    }
    // console.log(copyCalcArr)
    this.result = copyCalcArr[0]
  }
  protected rangeFactory(start: IAllPosition, end: IAllPosition) {
    let range = this.factory(Range) as Range;
    range.setValue(start, end);
    return range;
  }

  protected getValue(val: TAllTypes) {
    if (val.type === 'value') {
      return [val];
    }
    if (val.type === 'position' || val.type === 'rowPosition' || val.type === 'colPosition') {
      return this.getPositionValue(val);
    }
    if (__ENV__ === 'development') {
      console.log(``)
    }
    return [val];
  }

  protected matchFuncCalculate(func: string, vals: TAllTypes[]) {
    let funcInstance = this.functionManage.getFunc(func);
    return funcInstance.excute(vals)
    if (typeof this.funcCalculate[func] !== 'function') {
      throw new FormulaError('#NAME?', { info: { funcName: func, msg: 'calculat function not found!' } });
    }
    let result = this.funcCalculate[func](vals);
    if (!Array.isArray(result)) {
      throw new FormulaError('#NAME?', { info: { funcName: func, msg: 'calculat result is not Array!' } });
    }
    return result;
  }
}